﻿using System;
using System.ComponentModel.Composition;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reflection;
using AlanAamy.Net.TemperatureConverter.Infrastructure.Constants;
using AlanAamy.Net.TemperatureConverter.Models;
using log4net;

namespace AlanAamy.Net.TemperatureConverter.Services
{
    public interface ISequenceObserverController
    {
        void RewireSequenceObservers(UnitConversionMode conversionMode,
            TemperatureUnit currentTemperatureUnit, IScheduler subscribeScheduler, IScheduler observeScheduler);
        void ToggleUnWireSequenceObservers();

    }

    [Export(typeof(ISequenceObserverController))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class SequenceObserverController : ISequenceObserverController
    {
        private static ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        private readonly IRightSequenceObserverModel _rightSequenceObserverModel;
        private readonly ILeftSequenceObserverModel _leftSequenceObserverModel;
        private IDisposable _disposableLeftSequenceObserver;
        private IDisposable _disposableRightSequenceObserver;

        [ImportingConstructor]
        public SequenceObserverController(IRightSequenceObserverModel rightSequenceObserverModel, 
            ILeftSequenceObserverModel leftSequenceObserverModel)
        {
            _rightSequenceObserverModel = rightSequenceObserverModel;
            _leftSequenceObserverModel = leftSequenceObserverModel;
        }

        public void RewireSequenceObservers(UnitConversionMode conversionMode,TemperatureUnit currentTemperatureUnit,IScheduler subscribeScheduler,IScheduler observeScheduler)
        {
            Log.Debug("SequenceObserverController RewireSequenceObservers");
            if (conversionMode == UnitConversionMode.RightToLeft && currentTemperatureUnit == _rightSequenceObserverModel.Unit)
            {
                //Create LeftSequenceObserver and Dispose RightSequenceObserver
                if (_disposableRightSequenceObserver != null) _disposableRightSequenceObserver.Dispose();
                Log.Debug("RightSequenceObserver UnSubscribed");
                _disposableLeftSequenceObserver = _rightSequenceObserverModel.GetObservable().SubscribeOn(subscribeScheduler)
                    .ObserveOn(observeScheduler).Subscribe(onNext:
                        delegate
                        {
                            ChangeModelSequenceValue(conversionMode, currentTemperatureUnit);
                        });
            }
            else if (conversionMode == UnitConversionMode.LeftToRight && currentTemperatureUnit == _leftSequenceObserverModel.Unit)
            {
                //Create RightSequenceObserver and Dispose LeftSequenceObserver
                if (_disposableLeftSequenceObserver != null) _disposableLeftSequenceObserver.Dispose();
                Log.Debug("LeftSequenceObserver UnSubscribed");

                _disposableRightSequenceObserver = _leftSequenceObserverModel.GetObservable().SubscribeOn(subscribeScheduler)
                    .ObserveOn(observeScheduler).Subscribe(delegate
                {
                    ChangeModelSequenceValue(conversionMode, currentTemperatureUnit);
                });
            }
        }

        private void ChangeModelSequenceValue(UnitConversionMode conversionMode, TemperatureUnit currentTemperatureUnit)
        {
            if (conversionMode == UnitConversionMode.RightToLeft)
            {
                switch(currentTemperatureUnit)
                {
                    case TemperatureUnit.Celsius:
                        _leftSequenceObserverModel.Value = 9 * (_rightSequenceObserverModel.Value / 5) + 32;
                         break;
                     case TemperatureUnit.Farenheit:
                         _leftSequenceObserverModel.Value = 5 * (_rightSequenceObserverModel.Value - 32M) / 9;
                        break;
                }

                Log.Info("_rightSequenceObserverModel.Value : " + _rightSequenceObserverModel.Value +
                    " => _leftSequenceObserverModel.Value: " + _leftSequenceObserverModel.Value);
            }

            if (conversionMode == UnitConversionMode.LeftToRight)
            {
                switch (currentTemperatureUnit)
                {
                    case TemperatureUnit.Celsius:
                        _rightSequenceObserverModel.Value= 9 * (_leftSequenceObserverModel.Value / 5M) + 32;
                        break;
                    case TemperatureUnit.Farenheit:
                        _rightSequenceObserverModel.Value = 5 * (_leftSequenceObserverModel.Value - 32M) / 9;
                        break;
                }
                Log.Info("_leftSequenceObserverModel.Value : " + _leftSequenceObserverModel.Value +
                    " => _rightSequenceObserverModel.Value: " + _rightSequenceObserverModel.Value);
            }
        }

        /// <summary>
        /// Toggles the leftSequenceObserverModel and rightSequenceObserverModel temperature units
        /// Unsubscribe both the observers
        /// convert temperature mode Left to Right
        /// </summary>
        public void ToggleUnWireSequenceObservers()
        {
            if (_disposableRightSequenceObserver != null) _disposableRightSequenceObserver.Dispose();
            if (_disposableLeftSequenceObserver != null) _disposableLeftSequenceObserver.Dispose();
            _leftSequenceObserverModel.ToggleUnit();
            _rightSequenceObserverModel.ToggleUnit();
            ChangeModelSequenceValue(UnitConversionMode.LeftToRight, _leftSequenceObserverModel.Unit);
        }
    }
}
